package org.jing.core.lang;

import org.jing.core.lang.data.Carrier;
import org.jing.core.logger.sys.SingleLogger;
import org.jing.core.util.ClassUtil;
import org.jing.core.util.StringUtil;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;

/**
 * Description: <br>
 *
 * @author bks <br>
 * @since 2023-09-18 <br>
 */
@SuppressWarnings({ "WeakerAccess", "UnusedReturnValue", "BooleanMethodIsAlwaysInverted", "unused" })
public class JingInitializer {
    private static final HashMap<Class<? extends BaseInitializer>, BaseInitializer> INIT_MAP = new HashMap<>();

    private volatile static Carrier configC = null;

    public static <T extends BaseInitializer> T initialize(Class<T> type) {
        // 从未初始化过, 尝试使用根配置文件进行初始化
        if (!INIT_MAP.containsKey(type)) {
            Carrier configC = getRootConfig();
            if (null == configC) {
                INIT_MAP.put(type, null);
                throw new JingException("{} must be configured", type);
            }
            try {
                T instance = initialize(type, configC);
                INIT_MAP.put(type, instance);
                return instance;
            }
            catch (Throwable t ) {
                INIT_MAP.put(type, null);
                throw new JingException(t, "{} must be configured", type);
            }
        }
        T instance = (T) INIT_MAP.get(type);
        if (null == instance) {
            throw new JingException("{} must be configured", type);
        }
        return instance;
    }

    public static <T extends BaseInitializer> T initialize(Class<T> type, Carrier configC) {
        T instance = (T) INIT_MAP.get(type);
        if (null == instance) {
            instance = reInitialize(type, configC);
        }
        return instance;
    }

    public static <T extends BaseInitializer> T reInitialize(Class<T> type, Carrier configC) {
        try {
            T initializer = ClassUtil.createInstance(type);
            initializer.start();
            Class<? extends BaseConfiguration> configClass = (Class<? extends BaseConfiguration>) ((ParameterizedType) initializer.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
            BaseConfiguration config = configC.serialize(configClass);
            for (Field f$ : ClassUtil.getFields(initializer.getClass())) {
                if ("config".equals(f$.getName())) {
                    f$.setAccessible(true);
                    f$.set(initializer, config);
                }
            }
            initializer.before();

            initializer.execute();

            initializer.after();

            INIT_MAP.put(type, initializer);

            return initializer;
        }
        catch (Throwable t) {
            throw new JingException(t, "failed to initialize: {}", type.getName());
        }
    }

    public static <T extends BaseInitializer> boolean checkInstance(Class<T> type) {
        // 从未初始化过, 尝试使用根配置文件进行初始化
        if (!INIT_MAP.containsKey(type)) {
            Carrier configC = getRootConfig();
            if (null == configC) {
                INIT_MAP.put(type, null);
                return false;
            }
            try {
                T instance = initialize(type, configC);
                INIT_MAP.put(type, instance);
                return true;
            }
            catch (JingException e) {
                throw e;
            }
            catch (Throwable t) {
                INIT_MAP.put(type, null);
                return false;
            }
        }
        return null != INIT_MAP.get(type);
    }

    public static <T extends BaseInitializer> T getInstance(Class<T> type) {
        if (!checkInstance(type)) {
            throw new JingException("{} must be initialized", type);
        }
        return (T) INIT_MAP.get(type);
    }

    public static <T extends BaseInitializer> T enableInstance(Class<T> type) {
        if (!checkInstance(type)) {
            initialize(type);
        }
        return (T) INIT_MAP.get(type);
    }

    public static Carrier getJingConfigurationByXml() {
        return getJingConfigurationBySuffix("xml");
    }

    public static Carrier getJingConfigurationByJson() {
        return getJingConfigurationBySuffix("json");
    }

    public static Carrier getJingConfigurationByProperties() {
        return getJingConfigurationBySuffix("properties");
    }

    private static Carrier getJingConfigurationBySuffix(String suffix) {
        try (InputStream reader = getInputStreamBySuffix(suffix)) {
            switch (suffix) {
                case "xml": return Carrier.parseXml(StringUtil.readFromInputStream(reader));
                case "json": return Carrier.parseJson(StringUtil.readFromInputStream(reader));
                case "properties": return Carrier.parseProperties(StringUtil.readFromInputStream(reader));
                default: return null;
            }
        }
        catch (JingException e) {
            throw e;
        }
        catch (Throwable t) {
            throw new JingException(t, "failed to get " + suffix + " configuration file");
        }
    }

    private static InputStream getInputStreamBySuffix(String suffix) {
        try {
            InputStream inputStream = JingInitializer.class.getClassLoader().getResourceAsStream("jing-system." + suffix);
            if (null == inputStream) {
                inputStream = JingInitializer.class.getClassLoader().getResourceAsStream("jing." + suffix);
                if (null == inputStream) {
                    File file = new File("config" + File.separator + "jing-system." + suffix);
                    if (!file.exists() || !file.isFile()) {
                        file = new File("config" + File.separator + "jing." + suffix);
                    }
                    if (!file.exists() || !file.isFile()) {
                        throw new JingException("jing." + suffix + " or jing-system." + suffix + " not found");
                    }
                    SingleLogger.log("read configuration by {}", file.getAbsolutePath());
                    inputStream = new FileInputStream(file);
                }
                else {
                    SingleLogger.log("read configuration by classpath:jing." + suffix);
                }
            }
            else {
                SingleLogger.log("read configuration by classpath:jing-system." + suffix);
            }
            return inputStream;
        }
        catch (JingException e) {
            throw e;
        }
        catch (Throwable t) {
            throw new JingException(t, "failed to get " + suffix + " configuration file");
        }
    }

    public synchronized static Carrier getRootConfig() {
        if (null != configC) return configC;
        try {
            configC = getJingConfigurationByXml();
        }
        catch (Throwable ignored) {}
        if (null == configC) {
            try {
                configC = getJingConfigurationByJson();
            }
            catch (Throwable ignored) {}
        }
        if (null == configC) {
            try {
                configC = getJingConfigurationByProperties();
            }
            catch (Throwable ignored) {}
        }
        if (null == configC) {
            throw new JingException("failed to find any config file, check jing.xml or jing-system.xml at classpath or config path");
        }
        return configC;
    }

    public synchronized static Carrier getRootConfigWithoutCheck() {
        return configC;
    }

    public synchronized static void setRootConfig(Carrier configC) {
        JingInitializer.configC = configC;
    }
}
